package cc.autoapi.pucong.auto.flow.provider.aop.manager;


import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.aop.TaskAopInfoVo;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.exec.DataBus;
import cc.autoapi.pucong.auto.flow.core.root.exception.AutoFlowException;
import cc.autoapi.pucong.auto.flow.core.root.selfenum.CommonBoolenEnum;
import cc.autoapi.pucong.auto.flow.core.root.utils.ThreadPoolUtils;
import cc.autoapi.pucong.auto.flow.core.root.utils.UUID;
import cc.autoapi.pucong.auto.flow.persistence.po.aop.ProjectAopInfoPo;
import cc.autoapi.pucong.auto.flow.persistence.po.api.ApiInfoPo;
import cc.autoapi.pucong.auto.flow.persistence.po.sys.GroupInfoPo;
import cc.autoapi.pucong.auto.flow.persistence.repository.api.ApiInfoService;
import cc.autoapi.pucong.auto.flow.persistence.utils.UserRequest;
import cc.autoapi.pucong.auto.flow.persistence.utils.jdbc.DatabaseUtils;
import cc.autoapi.pucong.auto.flow.provider.aop.entity.ProjectAopInfoBeanConvert;
import cc.autoapi.pucong.auto.flow.provider.aop.entity.ProjectAopRuleBeanConvert;
import cc.autoapi.pucong.auto.flow.provider.aop.entity.dto.ProjectAopInfoInsertDto;
import cc.autoapi.pucong.auto.flow.provider.aop.entity.dto.ProjectAopInfoSelectDto;
import cc.autoapi.pucong.auto.flow.provider.aop.entity.dto.ProjectAopInfoUpdateDto;
import cc.autoapi.pucong.auto.flow.provider.aop.entity.vo.ProjectAopFitterApiVo;
import cc.autoapi.pucong.auto.flow.provider.aop.entity.vo.ProjectAopInfoVo;
import cc.autoapi.pucong.auto.flow.provider.aop.entity.vo.ProjectAopRuleExecVo;
import cc.autoapi.pucong.auto.flow.provider.aop.entity.vo.ProjectAopRuleVo;
import cc.autoapi.pucong.auto.flow.provider.group.service.GroupInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;


/**
 * AOP信息 service实现类
 *
 * @author pucong
 * @url https://gitee.com/pucong
 * @create 2023-11-21
 */
@Service
@Slf4j
public class AopInfoManager {

    @Autowired
    private AopRuleManager aopRuleManager;

    @Autowired
    private GroupInfoService groupInfoService;

    @Resource
    private ApiInfoService apiInfoService;

    public List<ProjectAopInfoVo> getProjectAopInfoList(ProjectAopInfoSelectDto dto) {
        List<Object> params = new ArrayList<>();
        StringBuffer sqlBf = new StringBuffer();
        sqlBf.append("select * from auto_flow_aop_info where 1=1 ");
        if (StringUtils.isNotBlank(dto.getKeywords())) {
            sqlBf.append(" and aop_name like ? ");
            params.add("%" + dto.getKeywords() + "%");
        }
        if (dto.getAopPosition() != null) {
            sqlBf.append(" and aop_position = ? ");
            params.add(dto.getAopPosition());
        }
        if (dto.getAopStatus() != null) {
            sqlBf.append(" and aop_status = ? ");
            params.add(dto.getAopStatus());
        }
        sqlBf.append(" order by create_time asc");
        List<ProjectAopInfoVo> projectAopInfos = DatabaseUtils.execQueryListSql(params, sqlBf.toString(), ProjectAopInfoVo.class);
        return projectAopInfos;
    }


    public ProjectAopInfoVo getProjectAopInfoById(String id) {
        List<Object> params = new ArrayList<>();
        StringBuffer sqlBf = new StringBuffer();
        sqlBf.append("select * from auto_flow_aop_info where 1=1 and uuid = ?");
        params.add(id);
        ProjectAopInfoPo aopInfoPo = DatabaseUtils.execQueryOneSql(params, sqlBf.toString(), ProjectAopInfoPo.class);

        // 查询api信息
        ApiInfoPo byId = apiInfoService.getById(aopInfoPo.getApiId());
        ProjectAopInfoVo aopInfoVo = ProjectAopInfoBeanConvert.INSTANCE.do2vo(aopInfoPo);
        aopInfoVo.setApiName(byId.getApiName());
        return aopInfoVo;
    }


    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public int insertProjectAopInfo(ProjectAopInfoInsertDto dto) {
        ProjectAopInfoPo projectAopInfoPo = ProjectAopInfoBeanConvert.INSTANCE.dto2do(dto);
        String uuid = UUID.uuid();
        projectAopInfoPo.setUuid(uuid);
        projectAopInfoPo.setAopStatus(0);
        projectAopInfoPo.setCreateBy(UserRequest.getLoginUser().getUuid());
        projectAopInfoPo.setCreateTime(new Date());
        Integer i = DatabaseUtils.saveAuto("auto_flow_aop_info", projectAopInfoPo);
        return i;
    }


    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public int updateProjectAopInfoById(String id, ProjectAopInfoUpdateDto dto) {
        ProjectAopInfoPo projectAopInfoPoUpdate = ProjectAopInfoBeanConvert.INSTANCE.dto2do(dto);
        projectAopInfoPoUpdate.setUuid(id);
        Integer i = DatabaseUtils.updateAutoById("auto_flow_aop_info", projectAopInfoPoUpdate);
        return i;
    }


    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public int batchDeleteProjectAopInfoByIds(List<String> ids) {
        if (ids == null || ids.size() < 1) {
            return 0;
        }
        StringBuffer sqlBf = new StringBuffer();
        sqlBf.append("delete from auto_flow_aop_info where uuid in (");
        for (int i = 0; i < ids.size(); i++) {
            sqlBf.append("?,");
        }
        sqlBf.deleteCharAt(sqlBf.length() - 1);
        sqlBf.append(")");
        List<Object> params = new ArrayList<>();
        params.addAll(ids);

        DatabaseUtils.execSql(params, sqlBf.toString(), Integer.class);

        // 删除匹配规则
        StringBuffer sqlBf2 = new StringBuffer();
        sqlBf2.append("delete from auto_flow_aop_rule where aop_id in (");
        for (int i = 0; i < ids.size(); i++) {
            sqlBf2.append("?,");
        }
        sqlBf2.deleteCharAt(sqlBf2.length() - 1);
        sqlBf2.append(")");
        List<Object> params2 = new ArrayList<>();
        params2.addAll(ids);

        DatabaseUtils.execSql(params2, sqlBf2.toString(), Integer.class);
        return ids.size();
    }

    public int startProjectAopByAopId(String aopId) {
        ProjectAopInfoVo projectAopInfoById = getProjectAopInfoById(aopId);
        if (projectAopInfoById != null) {
            // 修改状态为启用
            StringBuffer sqlBf = new StringBuffer();
            sqlBf.append("update auto_flow_aop_info set aop_status = 1 where uuid = ? ");
            List<Object> params = new ArrayList<>();
            params.add(aopId);
            DatabaseUtils.execSql(params, sqlBf.toString(), Integer.class);
            // 重新发布
            ThreadPoolUtils.executor(() -> {
                // 获取aop的可执行信息
                List<ApiInfoPo> fitterApiListByAopId = getFitterApiListByAopId(aopId, projectAopInfoById.getApiId());
                for (ApiInfoPo apiInfoPo : fitterApiListByAopId) {
                    List<TaskAopInfoVo> taskAopList = DataBus.getTaskAopList(apiInfoPo.getUuid());
                    if (taskAopList == null) {
                        taskAopList = new ArrayList<>();
                    }
                    List<TaskAopInfoVo> collect = taskAopList.stream().filter(taskAopInfoVo -> taskAopInfoVo.getUuid().equals(aopId)).collect(Collectors.toList());
                    if (collect.size() == 0) {
                        TaskAopInfoVo aopInfoVo = new TaskAopInfoVo();
                        aopInfoVo.setUuid(projectAopInfoById.getUuid());
                        aopInfoVo.setApiId(projectAopInfoById.getApiId());
                        aopInfoVo.setAopPosition(projectAopInfoById.getAopPosition());
                        aopInfoVo.setAopName(projectAopInfoById.getAopName());
                        taskAopList.add(aopInfoVo);
                    }
                    DataBus.setTaskAopList(apiInfoPo.getUuid(), taskAopList);
                }
            });
        }
        return 0;
    }


    public int stopProjectAopByAopId(String aopId) {
        ProjectAopInfoVo projectAopInfoById = getProjectAopInfoById(aopId);
        if (projectAopInfoById != null) {
            // 修改状态为禁用
            StringBuffer sqlBf = new StringBuffer();
            sqlBf.append("update auto_flow_aop_info set aop_status = 0 where uuid = ? ");
            List<Object> params = new ArrayList<>();
            params.add(aopId);
            DatabaseUtils.execSql(params, sqlBf.toString(), Integer.class);
            // 重新发布
            ThreadPoolUtils.executor(() -> {
                Set<String> allTaskAopKeyList = DataBus.getAllTaskAopKeyList();
                for (String s : allTaskAopKeyList) {
                    List<TaskAopInfoVo> taskAopList = DataBus.getTaskAopList(s);
                    List<TaskAopInfoVo> collect = taskAopList.stream().filter(taskAopInfoVo -> taskAopInfoVo.getUuid().equals(aopId)).collect(Collectors.toList());
                    taskAopList.removeAll(collect);
                }
            });
        }
        return 0;
    }

    public List<ProjectAopFitterApiVo> getFitterApiTreeByAopId(String aopId) {
        ProjectAopInfoVo projectAopInfoById = getProjectAopInfoById(aopId);
        if (projectAopInfoById == null) {
            throw new AutoFlowException("传入参数错误");
        }
        List<ProjectAopFitterApiVo> resules = new ArrayList<>();
        // 查询匹配的api
        List<ApiInfoPo> list = getFitterApiListByAopId(aopId, projectAopInfoById.getApiId());
        for (ApiInfoPo apiInfoPo : list) {
            ProjectAopFitterApiVo vo = new ProjectAopFitterApiVo();
            vo.setUuid(apiInfoPo.getUuid());
            vo.setNodeName(apiInfoPo.getApiName());
            vo.setApiCode(apiInfoPo.getApiCode());
            vo.setApiDesc(apiInfoPo.getApiDesc());
            vo.setApiType(apiInfoPo.getApiType());
            vo.setNodeType(1);
            vo.setParentId(apiInfoPo.getGroupId());
            resules.add(vo);
        }
        // 查询所属分组
        if (resules.size() > 0) {
            List<String> groupIds = list.stream().map(ApiInfoPo::getGroupId).collect(Collectors.toList());
            List<GroupInfoPo> groupInfos = groupInfoService.listByIds(groupIds);
            for (GroupInfoPo groupInfo : groupInfos) {
                ProjectAopFitterApiVo vo = new ProjectAopFitterApiVo();
                vo.setUuid(groupInfo.getUuid());
                vo.setNodeName(groupInfo.getName());
                vo.setNodeType(0);
                vo.setParentId("");
                resules.add(vo);
            }
        }
        return resules;
    }

    public List<ApiInfoPo> getFitterApiListByAopId(String aopId, String execApiId) {
        ProjectAopInfoVo projectAopInfoById = getProjectAopInfoById(aopId);
        if (projectAopInfoById == null) {
            throw new AutoFlowException("传入参数错误");
        }
        List<ApiInfoPo> list = apiInfoService.list(null, null, CommonBoolenEnum.是);

        List<ApiInfoPo> resules = getFitterApiListByAopId(aopId, execApiId, list);
        return resules;
    }

    public List<ApiInfoPo> getFitterApiListByAopId(String aopId, String execApiId, List<ApiInfoPo> list) {
        List<ApiInfoPo> resules = new ArrayList<>();
        // 查询规则
        List<ProjectAopRuleVo> projectAopRuleList = aopRuleManager.getProjectAopRuleList(aopId);
        List<ProjectAopRuleExecVo> ruleExecs = ProjectAopRuleBeanConvert.INSTANCE.vo2execVos(projectAopRuleList);

        for (ApiInfoPo apiInfoPo : list) {
            if (execApiId.equals(apiInfoPo.getUuid())) {
                continue;
            }
            Boolean success = false;
            String urlKey = apiInfoPo.getApiCode();
            for (ProjectAopRuleExecVo rule : ruleExecs) {
                success = rule.checkRule(urlKey);
                if (!success) {
                    break;
                }
            }
            if (success) {
                resules.add(apiInfoPo);
            }
        }
        return resules;
    }

    public void initPublishAop() {
        ProjectAopInfoSelectDto dto = new ProjectAopInfoSelectDto();
        dto.setAopStatus(1);
        List<ProjectAopInfoVo> projectAopInfoList = getProjectAopInfoList(dto);
        List<ApiInfoPo> list = apiInfoService.list(null, null, CommonBoolenEnum.是);

        for (ProjectAopInfoVo aopInfoVo : projectAopInfoList) {
            // 获取aop的可执行信息
            List<ApiInfoPo> fitterApiListByAopId = getFitterApiListByAopId(aopInfoVo.getUuid(), aopInfoVo.getApiId(), list);
            for (ApiInfoPo apiInfoPo : fitterApiListByAopId) {
                List<TaskAopInfoVo> taskAopList = DataBus.getTaskAopList(apiInfoPo.getUuid());
                if (taskAopList == null) {
                    taskAopList = new ArrayList<>();
                }
                List<TaskAopInfoVo> collect = taskAopList.stream().filter(taskAopInfoVo -> taskAopInfoVo.getUuid().equals(aopInfoVo.getUuid())).collect(Collectors.toList());
                if (collect.size() == 0) {
                    TaskAopInfoVo taskAopInfoVo = new TaskAopInfoVo();
                    taskAopInfoVo.setUuid(aopInfoVo.getUuid());
                    taskAopInfoVo.setApiId(aopInfoVo.getApiId());
                    taskAopInfoVo.setAopPosition(aopInfoVo.getAopPosition());
                    taskAopInfoVo.setAopName(aopInfoVo.getAopName());
                    taskAopList.add(taskAopInfoVo);
                }
                DataBus.setTaskAopList(apiInfoPo.getUuid(), taskAopList);
            }
        }
    }
}
